import os, re, shutil
import rt
from cache import cache
from xml.dom.minidom import parse, parseString
from datetime import datetime, timedelta
from utils import fg, bg, td, tr, xval
from string import Template

class TimeLayout:
	key = ''
	times = []
	interval = 1
	
class Seria:
	name = ''
	timelayout = 0
	values = []

class forecast:
	geocode_url = 'http://geocoder.us/service/csv/geocode?zip=%s'
	forecast_url = "http://www.weather.gov/forecasts/xml/SOAP_server/ndfdXMLclient.php?whichClient=NDFDgen&zipCodeList=%s&product=time-series&maxt=maxt&mint=mint&temp=temp&wspd=wspd&wdir=wdir&wx=wx&rh=rh&snow=snow&wwa=wwa&sky=sky&appt=appt&Submit=Submit"
	winddir = ['N',       'NE',      'E',    'SE', 'S', 'SW', 'W', 'NW']
	windarr = ['&#8593;', '&#8599;', '&#8594;', '&#8600;', '&#8595;', '&#8601;', '&#8592;', '&#8598;']
	HOURS = 24*7
	
	def dump(o):
		for property, value in inspect.getmembers(o):
#			print(property, ": ", len(value))
			print(property)

	def getTimeLayouts(dom):
		timelayouts = {}
		starttime = datetime.max
		data = dom.firstChild.getElementsByTagName("data")[0]
		tlayouts = data.getElementsByTagName("time-layout")
		for tlayout in tlayouts:
			timelayout = TimeLayout()
			timelayout.key = tlayout.getElementsByTagName("layout-key")[0].firstChild.nodeValue
			timelayout.times = []
			m = re.search('k-p([0-9]+)h-', timelayout.key)
			timelayout.interval = int(m.group(1))

			times = tlayout.getElementsByTagName("start-valid-time")
			for t in times:
				timestr = t.firstChild.nodeValue[:19]
				tm = datetime.strptime(timestr, '%Y-%m-%dT%H:00:00')
				timelayout.times.append(tm)
				if tm < starttime:
					starttime = tm
			timelayouts[timelayout.key] = timelayout
		return timelayouts, starttime

	def getIntencity(intencity):
		if intencity == 'light':
			return 'LT'
		if intencity == 'very light':
			return 'lt'
		if intencity == 'moderate':
			return 'MO'
		if intencity == 'heavy':
			return 'HV'
		return '??'

	def parseWeatherConditions(node, timelayout):
		series = []
		seriaWeatherType = Seria()
		seriaWeatherType.name = 'Weather Type'
		seriaWeatherType.timelayout = timelayout
		seriaWeatherType.values = []
		
		seriaVisibility = Seria()
		seriaVisibility.name = 'Visibility'
		seriaVisibility.timelayout = timelayout
		seriaVisibility.values = []
		
		seriaSnowIntensity = Seria()
		seriaSnowIntensity.name = 'Snow Intensity'
		seriaSnowIntensity.timelayout = timelayout
		seriaSnowIntensity.values = []
		
		seriaRainIntensity = Seria()
		seriaRainIntensity.name = 'Rain Intensity'
		seriaRainIntensity.timelayout = timelayout
		seriaRainIntensity.values = []
		
		conditions = node.getElementsByTagName('weather-conditions')
		for cond in conditions:
			values = cond.getElementsByTagName('value')
			visibility = ''
			snowint = ''
			rainint = ''
			for v in values:
				wt = v.getAttribute('weather-type')
				intencity = v.getAttribute('intensity')
				
				if wt == 'snow' or wt == 'snow showers':
					snowint = forecast.getIntencity(intencity)
					
				if wt == 'rain' or wt == 'rain showers':
					rainint = forecast.getIntencity(intencity)
					
				vnodes = v.getElementsByTagName('visibility')
				if len(vnodes) > 0:
					if vnodes[0].hasChildNodes():
						visibility = vnodes[0].firstChild.nodeValue
						
			seriaSnowIntensity.values.append(snowint)
			seriaRainIntensity.values.append(rainint)
			seriaWeatherType.values.append(' ')
			seriaVisibility.values.append(visibility)
		
		series.append(seriaSnowIntensity)
		series.append(seriaRainIntensity)
#			series.append(seriaWeatherType)
		series.append(seriaVisibility)
		return series

	def parseValues(node, timelayout):
		seria = Seria()
		name = node.getElementsByTagName("name")[0]
		seria.name = name.firstChild.nodeValue
		seria.timelayout = timelayout
		seria.values = []
		
		values = node.getElementsByTagName('value')
		for v in values:
			nv = ''
			if v.hasChildNodes():
				nv = v.firstChild.nodeValue

			if seria.name.find('Temperature') >= 0:
				nv = "%3d" % ((int(nv) - 32) * 5 / 9)
			if seria.name == 'Relative Humidity':
				nv = int(nv)
				if nv < 0:
					nv = ''
			if seria.name == 'Wind Direction':
				nv = forecast.windarr[int(((360-int(nv)+22.5) % 360) / 45)]
			if seria.name == 'Snow Amount':
				if nv == '0':
					nv = ''
			seria.values.append(nv)
		return seria

	def download(url, fname):
		try:
			cache.download(url, fname, timeout = 30)
		except Exception as details:
			rt.log.error(details)
			if os.path.isfile(fname):
				os.remove(fname)
			raise

	def parseData(dom):
		timelayouts, starttime = forecast.getTimeLayouts(dom)
			
		series = []
		parameters = dom.firstChild.getElementsByTagName("parameters")[0]
		for node in parameters.childNodes:
			if node.nodeType != dom.ELEMENT_NODE:
				continue
				
			name = node.getElementsByTagName("name")[0]
			timelayout = timelayouts[node.getAttribute('time-layout')]

			if name.firstChild.nodeValue == 'Weather Type, Coverage, and Intensity':
				series += forecast.parseWeatherConditions(node, timelayout)
			else:
				seria = forecast.parseValues(node, timelayout)
				if seria.name.find('Daily') == 0:
					continue
				series.append(seria)
		return series, starttime
		
	def parseHead(dom):
		head = dom.getElementsByTagName('head')[0]
		product = head.getElementsByTagName('product')[0]
		creationdate = product.getElementsByTagName('creation-date')[0]
		refreshfrequency = creationdate.getAttribute('refresh-frequency')
		return creationdate.firstChild.nodeValue, refreshfrequency

	def createTable(series, timestart):
		
		table = [['' for row in range(0, forecast.HOURS + 1)] for i in range(0, len(series) + 2)]

		table[0][0] = 'Filled'
		table[1][0] = 'Day'
		table[2][0] = 'Hour'
		
		for i in range(2, forecast.HOURS + 1):
			tm = timestart + timedelta(hours = i - 2)
			hour = ' ' + tm.strftime('%H')
			day = ' ' + tm.strftime('%a')[:2]
			
			table[0][i] = 0
			table[1][i] = day
			table[2][i] = hour
			
		rx = 3
		for s in series:
			if s.name == 'Watches, Warnings, and Advisories':
				continue
			
			table[rx][0] = s.name
			table[rx][1] = 0 # empty flag
			for cx in range(0, len(s.timelayout.times)):
				delta = s.timelayout.times[cx] - timestart
				d = delta.days * 24 + int(delta.seconds / 3600) + 1
				if d > 0 and d < forecast.HOURS:
					if type(s.values[cx]) == int or len(s.values[cx]) > 0:
						table[rx][d] = s.values[cx]
						table[0][d] = 1
						table[rx][1] = 1
			rx += 1

		return table
		
	def printTable(table):
		for i in range(1, forecast.HOURS):
			if table[0][i] == 1:
				print(fg(str(table[1][i]).rjust(3), 'red'), end = '')
		print('')
		
		for i in range(2, len(table)):
			row = table[i]
			if row[0] == 'Filled':
				continue
			print(row[0].ljust(40), end = '')
			for i in range(1, forecast.HOURS):
				if table[0][i] == 1:
					print(str(row[i]).rjust(3), end = '')
			print('')
	
	def rowToHtml(table, rowNum, refRowNum, tdstyle = 'td'):
		
		if table[rowNum][1] == 0:
			return ''
		
		s = ''
		prevday = ''
		idx = 1
		
		style = ''
		if refRowNum != 0:
			style = tdstyle + '0'
		
		row = table[rowNum]
		refrow = table[refRowNum]
		t = td(row[0], 'left')
		for cx in range(2, forecast.HOURS + 1):
			if table[0][cx] == 1:
				if refRowNum != 0 and prevday != str(refrow[cx]):
					idx += 1
					style = tdstyle + str(idx % 2)
				fgcolor = ''
				if row[0] == 'Temperature':
					if len(row[cx]) > 0 and  int(row[cx]) < 0:
						fgcolor = 'blue'
						
				t += td(str(row[cx]), style, fgcolor)
				prevday = str(refrow[cx])
				
		return tr(t)
	
	def tableToHtml(table):

		s = '<table>\n'
		s += forecast.rowToHtml(table, 1, 1, 'th')
		s += forecast.rowToHtml(table, 2, 1, 'th')
			
		for rx in range(3, len(table)):
			s += forecast.rowToHtml(table, rx, 1, 'td')
		
		s += '</table>\n'

		return s
	
	def rowToText(table, rowNum, refRowNum, tdstyle = 'td'):
		
		if table[rowNum][1] == 0:
			return ''
		
		s = ''
		prevday = ''
		idx = 1
		
		style = ''
		if refRowNum != 0:
			style = tdstyle + '0'
		
		row = table[rowNum]
		refrow = table[refRowNum]
		t = row[0]
		
		if t == 'Day': t = 'DAY '
		if t == 'Temperature': t = 'TEMP'
		if t == 'Apparent Temperature': t = 'ATMP'
		if t == 'Wind Speed': t = 'WIND'
		if t == 'Wind Direction': t = 'WDIR'
		if t == 'Cloud Cover Amount': t = 'CLDS'
		if t == 'Relative Humidity': t = 'HUMY'
		if t == 'Rain Intensity': t = 'RAIN'
		if t == 'Visibility': t = 'VISB'

		if t == 'WDIR': return ''
		
		t += ' '
		
		color = ''
		for cx in range(2, forecast.HOURS + 1):
			if table[0][cx] == 1:
			
				if prevday != str(refrow[cx]):
					idx += 1
					if row[0] == 'Apparent Temperature':
						color = ['cyan', 'magenta'][idx % 2] 
						
					if row[0] == 'Day':
						color = ['green', 'red'][idx % 2] 
					
				t += fg(str(row[cx]).rjust(3), color)

				prevday = str(refrow[cx])
				
		return t + '\n'
		
	def tableToText(table):

		s = ''
		s += forecast.rowToText(table, 1, 1, 'th')
		s += forecast.rowToText(table, 2, 1, 'th')
			
		for rx in range(3, len(table)):
			s += forecast.rowToText(table, rx, 1, 'td')
		
		return s
		
	def isError(dom):
		errors = dom.getElementsByTagName("error")
		if len(errors) > 0:
			print(errors[0].nodeName)
			return True
		return False
	
	def getAddress(zip):
		godir = rt.settings['tmpdir'] + '/geocode/'
		if not os.path.exists(godir):
			os.mkdir(godir)
			
		url = forecast.geocode_url % zip
		fname = godir + '%s.xml' % zip
		
		if cache.download(url, fname, timeout = 365*24*60):
			rt.log.info(url + ' downloaded to ' + fname)
			
		f = open(fname, 'rt')
		s = f.read()
		f.close()
		
		return s
	
	def getForecastHtml(zip, output):
		url = forecast.forecast_url % zip
		fname = rt.settings['tmpdir'] + 'fcast%s.xml' % zip
		
		forecast.download(url, fname + '.tmp')
		rt.log.info(url + ' downloaded to ' + fname + '.tmp')
		dom = parse(fname + '.tmp')
		
		if forecast.isError(dom):
			os.remove(fname + '.tmp')
			rt.log.error(dom.toprettyxml())
			return ('<div>Forecast for zip code ' + zip + ' is not available</div>', False)
		
		shutil.copy(fname + '.tmp', fname)
		rt.log.info(fname + '.tmp copied to ' + fname)
		creationdate, refreshfrequency = forecast.parseHead(dom)
		series, starttime = forecast.parseData(dom)

		address = forecast.getAddress(zip)
		
		table = forecast.createTable(series, starttime)
		
		text = address
		if output == 'html':
			text = '<div>' + text + '</div>\n'
			text += forecast.tableToHtml(table)
			text += '<br>'
		else:
			text += '\n'
			text += forecast.tableToText(table)
			text = '\033[2J' + text + '\n'
		
		rt.log.info(zip + ' text generated')
		return (text, True)
	
	def get_for_zip(req, hours, output):
		print(hours)
		forecast.HOURS = hours
		zips = req.split('/')[3];
		body = ''
		for zip in zips.split(','):
			(text, hr) = forecast.getForecastHtml(zip, output)
			if hr == False:
				return None
			body += text;
			
		if output == 'html':
			with open(rt.settings['bindir'] + '/page.html') as f:
				t = Template(f.read())
			d = dict(body = body, title = zips)
			body = t.safe_substitute(d)

		return body
